package pl.ps.wi.monitoring.station;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Date;

import pl.ps.wi.monitoring.station.interfaces.CardStatusListener;
import pl.ps.wi.monitoring.station.interfaces.LogginAttemptListener;
import pl.ps.wi.monitoring.station.services.DoorLock;

import pl.ps.wi.monitoring.station.services.VisitorCount;
import pl.ps.wi.monitoring.station.interfaces.VisitorsLimitExceeded;
import pl.ps.wi.monitoring.station.services.FaceRecognition;
import pl.ps.wi.monitoring.station.services.Recorder;

 /**
* @package pl.ps.wi.monitoring.station

* @brief Główny pakiet zawierający klasy niezbędne do funkcjonowania programu. 
* @file CardActions.java
*/
 /**
* @brief Klasa jest odpowiedzalna za czytnik kart i obsługę karty
*
* @date 2010.05.19
* @version 1.00.00

 */
public class CardActions implements LogginAttemptListener, CardStatusListener {
        //! odpowiada za połączenie z bazą danych
        private DBConnector conn;
        //! połączenie z klasą odpowiedzialną za otworzenie drzwi
	private DoorLock dors;

/**
 * Konstruktor.
 * @param conn obiekt, który odpowiada za połączenie z bazą danych
 *
 * 
 */
	public CardActions(DBConnector conn) {
		this.conn = conn;
		try {
			dors = new DoorLock();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

/**
 * Na podstawie parametrów weryfikuje kartę użytkownika
 * @param pesel - jest niepowtarzalnym identyfikatorem uzytkownika
 * @param pin - jest pinem podawanym do uzytkownika. Dopasowanym do ID każdego użytkownika
 * Metoda porównuje skrót z pinu z wpisem w bazie danych. Zgodność uruchamia moduł rozpoznawania twarzy, a następnie otwiera rygiel i wysyła SMS o powodzeniu. W przeciwnym razie wysyła SMS o nieudanym dostępie. W obu przypadkach dokonuje odpowiedniego wpisu do bazy danych.
 */
	@Override
	public void onLogginAttempt(String pesel, String pin) {
		try {
			String hash = Utils.computeSum(pin);
			System.out.println("\nPESEL: " + pesel + "\nPIN: " + pin);

			int person = conn.verifyCard(1, pesel, hash.trim());
			if (person > -1) {
				
				VisitorCount vc = new VisitorCount(conn.getConfiguration());
				if(vc.singleQuery() == 1) { // tylko jedna osoba może wejść
					FaceRecognition fares = new FaceRecognition(conn.getConfiguration());

					if(fares.query(person)) { // czy osoba widoczna na zdjęciu to ta posługująca się daną kartą

						setCardStatus("ACCESS GRANTED");
						System.out.println("Access granted");
						Phone.sendAccessGrantedMessage();
						try {
							dors.open();
							Recorder r = new Recorder(conn.getConfiguration());
							r.start();

							vc.addObserver(new VisitorsLimitExceeded() {
								public void notify(int visitors) {
									// poinformować o naruszeniu zasad
									Phone.sendAccessRulesViolationMessage();
									System.out.println("Access rules violation");
									setCardStatus("ACCESS RULES VIOLATION");
								}
							});
							vc.watch(1); // tylko jedna osoba może być w pomieszczeniu
						} catch (Exception e) {
							e.printStackTrace();
						}	

					} else {
						Phone.sendAccessDiedMessage();
						System.out.println("Access denied");
						setCardStatus("ACCESS DENIED");
					}
				} else {
					Phone.sendAccessDiedMessage();
					System.out.println("Access denied");
					setCardStatus("ACCESS DENIED");
				}
			} else {
				Phone.sendAccessDiedMessage();
				System.out.println("Access denied");
				setCardStatus("ACCESS DENIED");
			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

/**
 * @see setCardStatus(String status)
 * Wywoływana w klasie CardService przez funkcję fireCardInsert()
 */
	@Override
	public void onCardInset() {
		System.out.println("Podaj PIN: ");
		setCardStatus("INSERT");
	}
/**
 * @see setCardStatus(String status)
 */
	@Override
	public void onCardRemove() {
		setCardStatus("REMOVED");
	}

/**
 * Zapisuje zdarzenie związane z czytnikiem kart do bazy danych powiązane z aktualnym czasem. Wywoływana wyłącznie przez onCardRemove() i onCardInsert().
 * @param status - "REMOVED" lub "INSERT".
 */
	private void setCardStatus(String status) {
		try {
			int timeID = conn.insertTimeStamp(new Date());
			conn.insertCardState(timeID, 1, status);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
